/**
 * 监控组件基类
 * 基类包含<左侧折线图>，<右侧table>，<侧边指标基类>
 * 结构如下：
 *
 * |-----------| |------------------|
 * | FSMonitor | |  SessionMonitor  |
 * |-----------| |------------------|
 * |--------------------------------|
 * |          BaseComponent         |
 * |       |-----------------|      |
 * |       | OperationDrawer |      |
 * |       |-----------------|      |
 * |                                |
 * |--------------------------------|
 *
 * @author wujianx
 * @createdate 2020-12-20
 */
import { Line } from '@antv/g2plot';
import { SettingOutlined } from '@ant-design/icons';
import { Button, DatePicker, Select } from 'antd';
import { Moment } from 'moment';
import React from 'react';
import '../style.scss';
import {
  FSMonitorGraphInfo,
  FSMonitorTableInfo,
  SessionCenterGraphInfo,
  SessionCenterTableInfo,
} from 'admin/schema/RealTimeMonitor';
import moment from 'moment';
import { MonitorOperationDrawer } from './OperationDrawer';
import { ColFormItem } from 'components/UILibrary/colForm';
import { RadioChangeEvent } from 'antd/lib/radio';
import { JSONObject } from 'admin/schema/common';
import { PermissionType } from 'admin/schema/PermissionTypes';

export interface BaseMonitorProp {
  /**
   * realtime 实时模式
   * history 历史模式
   */
  mode: 'realtime' | 'history';
}

interface BaseMonitorState extends BaseMonitorProp {}

export abstract class BaseMonitor extends React.Component<BaseMonitorProp, BaseMonitorState> {
  /**
   * 图表横坐标刻度数量
   */
  protected tickCount: number;
  /**
   * 图表标题
   */
  protected title: string;

  /**
   * 渲染图表的时候数据选择字段
   */
  protected seriesField: string;

  /**
   * 图标和table的布局
   */
  protected layout: number[];

  /**
   * 配置表单参数
   */
  protected formConfig: ColFormItem[];

  /**
   * 配置表单按钮文字
   */
  protected btnCaption: string;

  /**
   * 当前展示的上线状态的监控项id
   */
  protected currentItemName: string;

  /**
   * 查询范围历史日期字符串
   */
  protected historyDate: string;

  /**
   * 查询范围实时日期字符串
   */
  protected realTimeDate: string;

  /**
   * 图标容器dom引用
   */
  private containerRef: React.RefObject<HTMLDivElement>;

  /**
   * 新增/编辑 指标抽屉引用
   */
  private operationRef: React.RefObject<MonitorOperationDrawer>;

  /**
   * 渲染为table的数据
   */
  protected tableData?: (FSMonitorTableInfo | SessionCenterTableInfo)[];

  /**
   * 折线图实例
   */
  private graph?: Line;

  /**
   * 定时器
   */
  private timer: number | null;

  /**
   * 实时选项值
   */
  protected realTimeTarget: 0 | 1 | 2 | 3 | 4 | 5;

  private refreshPromise: Promise<void> | null;

  /**
   * 编辑指标时保存按钮权限校验的key
   */
  protected permissionType?: PermissionType;

  constructor(prop: BaseMonitorProp) {
    super(prop);
    this.containerRef = React.createRef();
    this.operationRef = React.createRef();
    this.seriesField = '';
    this.tickCount = 10;
    this.historyDate =
      moment(new Date().setDate(new Date().getDate() - 1)).format('YYYY-MM-DD') + ' 00:00:00';
    this.realTimeDate = '';
    this.title = '';
    this.currentItemName = '';
    this.timer = null;
    this.layout = [3.6, 3.5];
    this.formConfig = [];
    this.btnCaption = '';
    this.refreshPromise = new Promise(resolve => {
      resolve();
    });
    this.realTimeTarget = 0;
    this.state = {
      mode: 'realtime',
    };
  }

  componentDidMount() {
    const mode = this.state.mode;
    mode === 'realtime' && this.startPolling();
    mode === 'history' && this.refresh();
  }

  componentDidUpdate(prop: BaseMonitorProp) {
    const { state } = this;
    const checkMode = prop.mode === state.mode;
    if (checkMode) {
      return;
    }
    this.setState(
      {
        mode: prop.mode,
      },
      () => {
        this.forceUpdate();
      }
    );
  }

  componentWillUnmount() {
    this.state.mode === 'realtime' && this.killPolling();
  }

  /**
   * 重新获取数据，渲染界面
   */
  public refresh(): Promise<void> {
    let refreshPromise: Promise<void>;
    // eslint-disable-next-line prefer-const
    refreshPromise = this.getTableDataReq().then((tableData: any) => {
      if (this.refreshPromise !== refreshPromise) {
        return;
      }
      if (!tableData) {
        return;
      }
      this.currentItemName === '' &&
        (this.currentItemName = tableData[0].fsName || tableData[0].scName);
      this.tableData = tableData;
      this.forceUpdate();
      this.getGraphDataReq().then((graphData: any) => {
        if (this.graph) {
          this.graph.changeData(graphData);
        } else {
          const domContainer = this.containerRef.current;
          if (!domContainer) {
            return;
          }
          // eslint-disable-next-line @typescript-eslint/no-this-alias
          const that = this;
          const seriesField = this.seriesField;
          const line = (this.graph = new Line(domContainer as HTMLElement, {
            seriesField: that.seriesField,
            tooltip: {
              formatter: (data: any) => {
                const currentItemName = that.currentItemName;
                if (seriesField === 'fsName') {
                  return { name: data.fsName, value: data.value };
                }
                const name = data.scName;
                const isCaps = name === 'caps';
                return {
                  name: isCaps ? currentItemName + ' Caps' : currentItemName + ' 会话',
                  value: data.value,
                };
              },
            },
            data: graphData,
            xField: 'time',
            yField: 'value',
            xAxis: {
              tickCount: that.tickCount,
            },
            slider: {
              start: 0,
              end: 1,
            },
            color: val => {
              if (val.scName === 'caps') {
                return '#259B24';
              }
              return '#1890ff';
            },
          }));
          line.render();
        }
        this.tableData = tableData;
        this.refreshPromise = null;
      });
    });
    return (this.refreshPromise = refreshPromise);
  }

  /**
   * 开始轮询
   */
  public startPolling(): void {
    this.refresh();
    (this.timer as any) = setTimeout(() => {
      this.startPolling();
    }, 5000);
  }

  /**
   * 结束轮询
   */
  public killPolling(): void {
    clearTimeout(this.timer as number);
    this.timer = null;
  }

  /**
   * 获得图标数据的函数
   */
  protected getGraphDataReq(): Promise<FSMonitorGraphInfo[] | SessionCenterGraphInfo[]> {
    throw new Error('this method must to be implemented in the sub-class');
  }

  /**
   * 获得表格数据的函数
   */
  protected getTableDataReq(): Promise<(FSMonitorTableInfo | SessionCenterTableInfo)[]> {
    throw new Error('this method must to be implemented in the sub-class');
  }

  /**
   * 获得指标列表的api
   */
  protected getIndicatorsReq(): Promise<any> {
    throw new Error('this method must to be implemented in the sub-class');
  }

  /**
   * 编辑指标的函数
   */
  protected modifyIndicatorsReq(params: JSONObject[]): Promise<void> {
    throw new Error('this method must to be implemented in the sub-class');
  }

  /**
   * 渲染table
   */
  protected renderTable(): JSX.Element {
    throw new Error('this method must to be implemented in the sub-class');
  }

  /**
   * 历史日期变更
   */
  private onChangeHistoryDate(date: Moment | null): void {
    if (!date) {
      this.historyDate = '';
      return;
    }
    this.historyDate = date.format('YYYY-MM-DD HH:mm:ss');
    this.refresh();
  }

  /**
   * 实时时间段变更
   */
  private onRealTimeRangeChange(val: number, needRefresh = true): void {
    this.realTimeTarget = val as 0 | 1 | 2 | 3 | 4 | 5;
    const now = new Date() as any;
    const oneMinute = 60 * 1000;
    const oneHour = 60 * 1000 * 60;
    const actionMap = {
      0: () => now - oneMinute * 2,
      1: () => now - oneMinute * 10,
      2: () => now - oneHour * 0.5,
      3: () => now - oneHour,
      4: () => now - oneHour * 3,
      5: () => moment(new Date().setDate(new Date().getDate() - 1)).format('YYYY-MM-DD HH:mm:ss'),
    };
    const func = actionMap[val as 0 | 1 | 2 | 3 | 4 | 5];
    func && (this.realTimeDate = moment(func()).format('YYYY-MM-DD HH:mm:ss'));
    needRefresh && this.refresh();
  }

  /**
   * 刷新当前实时时间
   */
  protected refreshRealTime() {
    this.onRealTimeRangeChange(this.realTimeTarget, false);
  }

  /**
   * 唤出编辑指标对话框
   */
  private showEditer(): void {
    this.operationRef.current?.show();
  }

  /**
   * 限制时间选择器选择范围
   */
  private disabledDate(date: Moment): boolean {
    const lastMonth = new Date().setDate(new Date().getDate() - 90);
    const nextDay = new Date().setDate(new Date().getDate() - 1);
    if (!date) {
      return false;
    }
    return date < moment(lastMonth) || date > moment(nextDay);
  }

  /**
   * 切换当前图表数据指标
   */
  protected handleCurrentItemNameChange(e: RadioChangeEvent, itemName: string): void {
    const checked = e.target.checked;
    if (!checked) {
      return;
    }
    this.currentItemName = itemName;
    this.forceUpdate();
    this.refresh();
  }

  render() {
    const { mode } = this.state;
    const { containerRef, operationRef, title } = this;
    const graphRoomWidth = this.layout[0] * 10 + '%';

    return (
      <div className="realtime-monitor-ins">
        <div className="monitor-title">
          {title}
          <div className="monitor-config">
            {mode === 'realtime' ? (
              <Select
                className="monitor-time-picker"
                defaultValue={0}
                options={realTimeRange}
                onChange={val => {
                  this.onRealTimeRangeChange(val as number);
                }}
              />
            ) : (
              <DatePicker
                className="monitor-time-picker"
                disabledDate={date => {
                  return this.disabledDate(date);
                }}
                defaultValue={moment(new Date().setDate(new Date().getDate() - 1))}
                onChange={date => {
                  this.onChangeHistoryDate(date);
                }}
              />
            )}
            <Button
              className="monitor-config-btn"
              shape="circle"
              icon={<SettingOutlined />}
              onClick={this.showEditer.bind(this)}
            />
          </div>
        </div>
        <div className="monitor-content">
          <div
            ref={containerRef}
            className="monitor-graph"
            id="graph-container"
            style={{ width: graphRoomWidth }}
          ></div>
          <div className="monitor-table-room">{this.renderTable()}</div>
        </div>
        <MonitorOperationDrawer
          ref={operationRef}
          formConfig={this.formConfig}
          btnCaption={this.btnCaption}
          permissionType={this.permissionType as PermissionType}
          fetchListApi={this.getIndicatorsReq}
          callback={() => {
            this.refresh();
          }}
          submitApi={this.modifyIndicatorsReq}
        />
      </div>
    );
  }
}

const realTimeRange = [
  {
    label: '近两分钟',
    value: 0,
  },
  {
    label: '近十分钟',
    value: 1,
  },
  {
    label: '近半小时',
    value: 2,
  },
  {
    label: '近一小时',
    value: 3,
  },
  {
    label: '近三小时',
    value: 4,
  },
  {
    label: '今天',
    value: 5,
  },
];
